/*
 * atomcaching URL protocol
 * Copyright (c) 2013
__author__ : Nguyentiennam2910@gmail.com
 */

#include "avformat.h"
#include "libavutil/avstring.h"
#include "libavutil/mem.h"
#include "url.h"
#define AV_ATOM_SEPARATOR "|"
#define CACHING_DIRECTORY "/home/ntn/Downloads/"

struct atomcaching_nodes {
    URLContext *uc;                ///< node's URLContext
    int64_t     size;              ///< url filesize
};

struct atomcaching_data {
    struct atomcaching_nodes *nodes;    ///< list of nodes to atomcaching
    size_t               length;   ///< number of cat'ed nodes
    size_t               current;  ///< index of currently read node
};

int localfile =0;
char savefile_dir[5000];

static av_cold int atomcaching_close(URLContext *h)
{
    
    int err = 0;
    size_t i;
    struct atomcaching_data  *data  = h->priv_data;
    struct atomcaching_nodes *nodes = data->nodes;

    for (i = 0; i != data->length; i++)
        err |= ffurl_close(nodes[i].uc);

    av_freep(&data->nodes);

    return err < 0 ? -1 : 0;
}

static av_cold int atomcaching_open(URLContext *h, const char *uri, int flags)
{
    
    char *node_uri = NULL, *tmp_uri, *tmp_filename, *ori_filename1, *ori_filename2 , *new_uri, * uri_process;
    char home_dir[100];
    char filename1[100];
    int err = 0, var_tmp=0, var_tmp1=0, j=0;
    int64_t size;
    size_t  len, i;
    URLContext *uc;
    struct atomcaching_data  *data = h->priv_data;
    struct atomcaching_nodes *nodes;

	memset(home_dir,'\0',strlen(home_dir));
	strcpy(home_dir,CACHING_DIRECTORY);
	tmp_filename = av_malloc(sizeof(char) * strlen(uri));
	ori_filename1 = av_malloc(sizeof(char) * strlen(uri));
	ori_filename2 = av_malloc(sizeof(char) * strlen(uri));
	new_uri = av_malloc(sizeof(char) * 1000);
	uri_process = av_malloc(sizeof(char) * 1000);
    strcpy(filename1, home_dir);

   	for (var_tmp=strlen(uri); var_tmp>=0; var_tmp--)
		if( uri[var_tmp] =='/') break;
	//new_uri = atomcatching:http://abc.com/"
	av_strlcpy(new_uri, uri, var_tmp+2);
	//new_uri = http://abc.com/	
	j = 0;
	while (new_uri[j] !=':'){
		j++;	
	}
	j = j+1;
	var_tmp1 = 0;	
	while ( 1){
		new_uri[var_tmp1] = new_uri[j];
		j++;
		var_tmp1 ++;
		if (j >= strlen(new_uri)) break;
	}
	new_uri[var_tmp1]='\0';
	// tmp_filename = goc.mp4
	for (var_tmp1=var_tmp+1; var_tmp1 <= strlen(uri); var_tmp1++)
		tmp_filename[var_tmp1-var_tmp-1] = uri[var_tmp1];	
	//ori_filename1= p-1.mp4, ori_filename2 = p-2.mp4
	j = 0;
	while(tmp_filename[j] !='.'){
		ori_filename1[j] = tmp_filename[j];
		j++;	
	}
	ori_filename1[j] = '-';
	ori_filename1[j+1] = '1';
	strcpy(ori_filename2, ori_filename1);
	ori_filename2[j+1] = '2';
	var_tmp1 = j+2;
	while(j<strlen(tmp_filename)){
		ori_filename1[var_tmp1] = tmp_filename[j];
		ori_filename2[var_tmp1] = tmp_filename[j];
		j++;	
		var_tmp1 ++;
	}
	ori_filename1[var_tmp1] = '\0';
	ori_filename2[var_tmp1] = '\0';
	//filename1= "/home/ntn/Downloads/p-1.mp4";
	var_tmp1=strlen(filename1);
	j = 0;	
	while (j<strlen(ori_filename1)){
		filename1[var_tmp1] = ori_filename1[j];
		var_tmp1++;
		j++;
	}
	filename1[var_tmp1]= '\0';
	//for caching
	strcpy(savefile_dir,filename1);
	//check file exist, filename1
    if (avio_check(filename1, AVIO_FLAG_READ) > 0) {
		//fina uri
		strcpy(uri_process, "atomcaching:");
		
		//uri_process = atomcaching:
		j = strlen(uri_process);
		var_tmp1 = 0;
		//uri_process = atomcaching:/home/ntn/Downloads/p-1.mp4|
		while(var_tmp1 < strlen(filename1)){
			uri_process[j] = filename1[var_tmp1];
			j++;
			var_tmp1++;	
		}	
		uri_process[j] = '|';
		//uri_process=atomcaching:/home/ntn/Downloads/p-1.mp4|http://abc.com/
		j++;
		var_tmp1 = 0;
		while(var_tmp1 < strlen(new_uri)){
			uri_process[j] = new_uri[var_tmp1]; 
			j++;
			var_tmp1++;	
		}		
		//uri_process=atomcaching:/home/ntn/Downloads/p-1.mp4|http://abc.com/p-2.mp4
		var_tmp1 = 0;
		while(var_tmp1 < strlen(ori_filename2)){
			uri_process[j] = ori_filename2[var_tmp1];
			j++;
			var_tmp1++;	
		}
		uri_process[j] = '\0';
		//copy to uri
		uri = uri_process;
		localfile = 1;
		av_log(NULL,AV_LOG_ERROR,"Atomheader file exist, Use atomcaching, uri=%s\n",uri_process);
		
	}
	else{
		//fina uri
		strcpy(uri_process, "atomcaching:");
		
		//uri_process = atomcaching:
		j = strlen(uri_process);
		var_tmp1 = 0;
		//uri_process = atomcaching:http://abc.com/

		while(var_tmp1 < strlen(new_uri)){
			uri_process[j] = new_uri[var_tmp1];
			j++;
			var_tmp1++;	
		}
		uri_process[j] = '\0';
		j = strlen(uri_process);
		var_tmp1 = 0;
		//uri_process = atomcaching:http://abc.com/p-1.mp4
		while(var_tmp1 < strlen(ori_filename1)){
			uri_process[j] = ori_filename1[var_tmp1];
			j++;
			var_tmp1++;	
		}	
		uri_process[j] = '|';
		//uri_process=atomcaching:atomcaching:http://abc.com/p-1.mp4|http://abc.com/
		j++;
		var_tmp1 = 0;
		while(var_tmp1 < strlen(new_uri)){
			uri_process[j] = new_uri[var_tmp1]; 
			j++;
			var_tmp1++;	
		}		
		//uri_process=atomcaching:atomcaching:http://abc.com/p-1.mp4|http://abc.com/p-2.mp4
		var_tmp1 = 0;
		while(var_tmp1 < strlen(ori_filename2)){
			uri_process[j] = ori_filename2[var_tmp1];
			j++;
			var_tmp1++;	
		}
		uri_process[j] = '\0';
		//copy to uri
		uri = uri_process;
		av_log(NULL,AV_LOG_ERROR,"Can't find atomheader file in local, play and save atomheader, uri=%s\n",uri_process);
		
	}
	
    av_strstart(uri, "atomcaching:", &uri);
    for (i = 0, len = 1; uri[i]; i++)
        if (uri[i] == *AV_ATOM_SEPARATOR)
            /* integer overflow */
            if (++len == UINT_MAX / sizeof(*nodes)) {
                av_freep(&h->priv_data);
                return AVERROR(ENAMETOOLONG);
            }

    if (!(nodes = av_malloc(sizeof(*nodes) * len))) {
        return AVERROR(ENOMEM);
    } else
        data->nodes = nodes;

    /* handle input */
    if (!*uri)
        err = AVERROR(ENOENT);

    for (i = 0; *uri; i++) {
        /* parsing uri */
        len = strcspn(uri, AV_ATOM_SEPARATOR);

        if (!(tmp_uri = av_realloc(node_uri, len+1))) {
            err = AVERROR(ENOMEM);
            break;
        } else
            node_uri = tmp_uri;

        av_strlcpy(node_uri, uri, len+1);
        uri += len + strspn(uri+len, AV_ATOM_SEPARATOR);

        /* creating URLContext */
        if ((err = ffurl_open(&uc, node_uri, flags,
                              &h->interrupt_callback, NULL)) < 0)
            break;

        /* creating size */
        if ((size = ffurl_size(uc)) < 0) {
            ffurl_close(uc);
            err = AVERROR(ENOSYS);
            break;
        }

        /* assembling */
        nodes[i].uc   = uc;
        nodes[i].size = size;
    }
    av_free(node_uri);
    data->length = i;

    if (err < 0)
        atomcaching_close(h);
    else if (!(nodes = av_realloc(nodes, data->length * sizeof(*nodes)))) {
        atomcaching_close(h);
        err = AVERROR(ENOMEM);
    } else
        data->nodes = nodes;
    return err;
}

static int atomcaching_read(URLContext *h, unsigned char *buf, int size)
{
FILE *f;
	
    int result, total = 0;
    struct atomcaching_data  *data  = h->priv_data;
    struct atomcaching_nodes *nodes = data->nodes;
    size_t i = data->current;
	
    while (size > 0) {
		//total result = strlen(file)       
		result = ffurl_read(nodes[i].uc, buf, size);
		//av_log(NULL,AV_LOG_ERROR," current=%d result=%d size=%d\n",i, result,size);
		// header atom, i=0(data->current),if localfile exist, don't create new header file in local
		if(i == 0 && localfile == 0){	
		f=fopen(savefile_dir,"a+");
		//av_log(NULL,AV_LOG_ERROR,"atomheader current=%d and size of buff=%d\n",data->current,strlen(buf));	
		fwrite(buf, sizeof(unsigned char),result,f);
		fclose(f);	
}
        if (result < 0)
            return total ? total : result;
        if (!result)
            if (i + 1 == data->length ||
                ffurl_seek(nodes[++i].uc, 0, SEEK_SET) < 0)
                break;
        total += result;
        buf   += result;
        size  -= result;
    }
		
    data->current = i;
	
    return total;
}

static int64_t atomcaching_seek(URLContext *h, int64_t pos, int whence)
{
    int64_t result;
    struct atomcaching_data  *data  = h->priv_data;
    struct atomcaching_nodes *nodes = data->nodes;
    size_t i;

    switch (whence) {
    case SEEK_END:
        for (i = data->length - 1;
             i && pos < -nodes[i].size;
             i--)
            pos += nodes[i].size;
        break;
    case SEEK_CUR:
        /* get the absolute position */
        for (i = 0; i != data->current; i++)
            pos += nodes[i].size;
        pos += ffurl_seek(nodes[i].uc, 0, SEEK_CUR);
        whence = SEEK_SET;
        /* fall through with the absolute position */
    case SEEK_SET:
        for (i = 0; i != data->length - 1 && pos >= nodes[i].size; i++)
            pos -= nodes[i].size;
        break;
    default:
        return AVERROR(EINVAL);
    }

    result = ffurl_seek(nodes[i].uc, pos, whence);
    if (result >= 0) {
        data->current = i;
        while (i)
            result += nodes[--i].size;
    }
    return result;
}

URLProtocol ff_atomcaching_protocol = {
    .name           = "atomcaching",
    .url_open       = atomcaching_open,
    .url_read       = atomcaching_read,
    .url_seek       = atomcaching_seek,
    .url_close      = atomcaching_close,
    .priv_data_size = sizeof(struct atomcaching_data),
};

